﻿using System.Collections;
using System.Reflection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using SqlSugar;
using Yitter.IdGenerator;
using Digitalmes.Domain;
using Digitalmes.Common.Utils;
using Digitalmes.Common.Extensions;

namespace Digitalmes.Sugar;

/// <summary>
/// Sugar 设置。
/// </summary>
public static class SugarSetup
{
    /// <summary>
    /// 注册基于 SqlSugar 配置的服务
    /// </summary>
    /// <param name="services"></param>
    public static void AddSqlSugarSetup(this IServiceCollection services, HostBuilderContext hostBuilderContext)
    {
        // 自定义 SqlSugar 雪花ID算法
        SnowFlakeSingle.WorkId = 1;
        StaticConfig.CustomSnowFlakeFunc = YitIdHelper.NextId;

        var dbOptions = hostBuilderContext.Configuration.GetSection("DbConnection").Get<DbConnectionOptions>();
        dbOptions!.ConnectionConfigs.ForEach(SetDbConfig);

        SqlSugarScope sqlSugar = new(dbOptions!.ConnectionConfigs.OfType<ConnectionConfig>().ToList(), db =>
        {
            dbOptions!.ConnectionConfigs.ForEach(config =>
            {
                var dbProvider = db.GetConnectionScope(config.ConfigId);
                SetDbAop(dbProvider, hostBuilderContext.HostingEnvironment);
            });
        });

        services.AddSingleton<ISqlSugarClient>(sqlSugar); // 单例注册
        services.AddScoped(typeof(SugarRepository<>)); // 仓储注册

        // 初始化数据库表结构及种子数据
        dbOptions.ConnectionConfigs.ForEach(config =>
        {
            InitDatabase(sqlSugar, config);
        });
    }

    /// <summary>
    /// 配置连接属性
    /// </summary>
    /// <param name="config"></param>
    public static void SetDbConfig(DbConnectionConfig config)
    {
        // 若 ConfigId 没有设置，会使用默认数据库标识。
        // 注：配置连接中的第一个不需要配置，其他的必须配置不同值。
        if (config.ConfigId == null)
        {
            config.ConfigId = SqlSugarConst.ConfigId;
        }

        var configureExternalServices = new ConfigureExternalServices
        {
            EntityNameService = (type, entity) => // 处理表
            {
                entity.IsDisabledDelete = true; // 禁止删除非 sqlsugar 创建的列
                // 只处理设置了[SugarTable]特性的表
                if (!type.IsDefined(typeof(SugarTable), false))
                    return;
               
                if (config.EnableUnderLine && !entity.DbTableName.Contains('_'))
                    entity.DbTableName = UtilMethods.ToUnderLine(entity.DbTableName); // 驼峰转下划线
            },
            EntityService = (type, column) => // 处理列
            {
                // 只处理设置了[SugarColumn]特性的列
                if (!type.IsDefined(typeof(SugarColumn), false))
                    return;

                if (new NullabilityInfoContext().Create(type).WriteState is NullabilityState.Nullable)
                    column.IsNullable = true;
                if (config.EnableUnderLine && !column.IsIgnore && !column.DbColumnName.Contains('_'))
                    column.DbColumnName = UtilMethods.ToUnderLine(column.DbColumnName); // 驼峰转下划线

                if (config.DbType == DbType.Oracle)
                {
                    if (type.PropertyType == typeof(long) || type.PropertyType == typeof(long?))
                        column.DataType = "number(18)";
                    if (type.PropertyType == typeof(bool) || type.PropertyType == typeof(bool?))
                        column.DataType = "number(1)";
                }
            },

            //DataInfoCacheService = new SugarMemoryCache(), // 设置二级缓存
        };

        config.ConfigureExternalServices = configureExternalServices;
        config.InitKeyType = InitKeyType.Attribute;
        config.IsAutoCloseConnection = true;
        config.MoreSettings = new ConnMoreSettings
        {
            IsAutoRemoveDataCache = true,
            IsAutoDeleteQueryFilter = true, // 启用删除查询过滤器
            IsAutoUpdateQueryFilter = true, // 启用更新查询过滤器
            SqlServerCodeFirstNvarchar = true, // 采用Nvarchar
        };
    }

    /// <summary>
    /// 配置Aop
    /// </summary>
    /// <param name="db"></param>
    public static void SetDbAop(SqlSugarScopeProvider db, IHostEnvironment env)
    {
        var config = db.CurrentConnectionConfig;

        // 设置超时时间
        db.Ado.CommandTimeOut = 30;

        // 打印SQL语句
        db.Aop.OnLogExecuting = (sql, pars) =>
        {
            // 仅开发环境显示
            if (env.IsDevelopment())
            {
                var originColor = Console.ForegroundColor;
                if (sql.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase))
                    Console.ForegroundColor = ConsoleColor.Green;
                if (sql.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) || sql.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase))
                    Console.ForegroundColor = ConsoleColor.Yellow;
                if (sql.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase))
                    Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} —— SQL Executing] {Environment.NewLine}{UtilMethods.GetSqlString(config.DbType, sql, pars)}{Environment.NewLine}");
                Console.ForegroundColor = originColor;
            }
        };

        db.Aop.OnError = ex =>
        {
            if (ex.Parametres == null) return;

            var originColor = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.DarkRed;
            var pars = db.Utilities.SerializeObject(((SugarParameter[])ex.Parametres).ToDictionary(it => it.ParameterName, it => it.Value));
            Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} —— SQL Error] {Environment.NewLine}{UtilMethods.GetSqlString(config.DbType, ex.Sql, (SugarParameter[])ex.Parametres)}{Environment.NewLine}");
            Console.ForegroundColor = originColor;
        };

        // 数据审计
        db.Aop.DataExecuting = (oldValue, entityInfo) =>
        {
            if (entityInfo.OperationType == DataFilterType.InsertByObject)
            {
                // 主键(long类型)且没有值的---赋值雪花Id
                if (entityInfo.EntityColumnInfo.IsPrimarykey && entityInfo.EntityColumnInfo.PropertyInfo.PropertyType == typeof(long))
                {
                    var id = entityInfo.EntityColumnInfo.PropertyInfo.GetValue(entityInfo.EntityValue);
                    if (id == null || (long)id == 0)
                        entityInfo.SetValue(YitIdHelper.NextId());
                }

                if (entityInfo.PropertyName == nameof(IAuditEntity.CreateTime))
                    entityInfo.SetValue(DateTime.Now);

                if (AppUtils.User != null)
                {
                    if (entityInfo.PropertyName == nameof(ITenantIdFilter.TenantId))
                    {
                        var tenantId0 = AppUtils.TenantId;
                        var tenantId = ((dynamic)entityInfo.EntityValue).TenantId;
                        if (tenantId != tenantId0)
                            entityInfo.SetValue(tenantId0);
                    }
                    if (entityInfo.PropertyName == nameof(IAuditEntity.CreateUser))
                    {
                        var createUser = ((dynamic)entityInfo.EntityValue).CreateUser;
                        if (string.IsNullOrEmpty(createUser))
                            entityInfo.SetValue(AppUtils.LoginUser);
                    }
                }
            }

            if (entityInfo.OperationType == DataFilterType.UpdateByObject)
            {
                if (entityInfo.PropertyName == nameof(IAuditEntity.UpdateTime))
                    entityInfo.SetValue(DateTime.Now);
                if (entityInfo.PropertyName == nameof(IAuditEntity.UpdateUser))
                    entityInfo.SetValue(AppUtils.LoginUser);
            }
        };

        // 配置实体假删除过滤器
        db.QueryFilter.AddTableFilter<IDeletedFilter>(u => u.IsDel == false);

        // 配置租户过滤器
        db.QueryFilter.AddTableFilter<ITenantIdFilter>(u => u.TenantId == AppUtils.TenantId);

        // 配置自定义过滤器
        SugarFilter.SetCustomEntityFilter(db);
    }

    /// <summary>
    /// 初始化数据库
    /// </summary>
    private static void InitDatabase(SqlSugarScope db, DbConnectionConfig config)
    {
        if (!config.EnableInitDb) 
            return;

        SqlSugarScopeProvider dbProvider = db.GetConnectionScope(config.ConfigId);

        // 创建数据库
        if (config.DbType != DbType.Oracle)
            dbProvider.DbMaintenance.CreateDatabase();

        // 获取所有实体表-初始化表结构
        var entityTypes = typeof(EntityBase).Assembly.GetTypes().Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false)).ToList();
        if (!entityTypes.Any()) return;

        foreach (var entityType in entityTypes)
        {
            var tAtt = entityType.GetCustomAttribute<TenantAttribute>();
            if (tAtt != null && tAtt.configId != config.ConfigId) 
                continue;

            if (tAtt == null && config.ConfigId.ToString() != SqlSugarConst.ConfigId) 
                continue;

            if (!entityType.IsDefined(typeof(SplitTableAttribute), false))
                dbProvider.CodeFirst.InitTables(entityType);
            else
                dbProvider.CodeFirst.SplitTables().InitTables(entityType);
        }

        if (!config.EnableInitSeed) 
            return;

        // 获取所有种子配置-初始化数据
        var seedDataTypes = typeof(ISqlSugarEntitySeedData<>).Assembly.GetTypes().Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass
            && u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(ISqlSugarEntitySeedData<>)))).ToList();
        if (!seedDataTypes.Any()) 
            return;

        foreach (var seedType in seedDataTypes)
        {
            var instance = Activator.CreateInstance(seedType);

            var hasDataMethod = seedType.GetMethod("HasData");
            var seedData = (hasDataMethod?.Invoke(instance, null) as IEnumerable)?.Cast<object>();
            if (seedData == null) 
                continue;

            var entityType = seedType.GetInterfaces().First().GetGenericArguments().First();
            var tAtt = entityType.GetCustomAttribute<TenantAttribute>();
            if (tAtt != null && tAtt.configId != config.ConfigId) 
                continue;

            if (tAtt == null && config.ConfigId.ToString() != SqlSugarConst.ConfigId) 
                continue;

            var entityInfo = dbProvider.EntityMaintenance.GetEntityInfo(entityType);
            if (entityInfo.Columns.Any(u => u.IsPrimarykey))
            {
                // 按主键进行批量增加和更新
                var storage = dbProvider.StorageableByObject(seedData.ToList()).ToStorage();
                storage.AsInsertable.ExecuteCommand();
                if (!entityType.IsDefined(typeof(IgnoreUpdateAttribute), false)) 
                    storage.AsUpdateable.ExecuteCommand();
            }
            else
            {
                // 无主键则只进行插入
                if (!dbProvider.Queryable(entityInfo.DbTableName, entityInfo.DbTableName).Any())
                    dbProvider.InsertableByObject(seedData.ToList()).ExecuteCommand();
            }
        }
    }
}
